﻿using Agile.Infra.Core.Exceptions;
using Agile.Infra.Core.Guard;
using System.Data;

namespace Agile.Infra.Repository;

public class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : BaseModel, new()
{
    private readonly IServiceProvider _serviceProvider;
    private readonly IUnitOfWork _unitOfWork;
    private readonly SqlSugarScope _sqlSugarClient;
    private readonly Guid _contextID;
    public IServiceProvider ServiceProvider { get { return _serviceProvider; } }
    public SqlSugarScope Db
    {
        get { return _sqlSugarClient; }
    }

    public BaseRepository(IUnitOfWork unitOfWork, IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
        _unitOfWork = unitOfWork;
        _sqlSugarClient = _unitOfWork.GetDbClient();
        //勿删
        _contextID = _sqlSugarClient.ContextID;
    }


    #region 新增

    public async Task<int> AddAsync(TEntity entity)
    {
        var insert = _sqlSugarClient.Insertable(entity);
        //这里你可以返回TEntity，这样的话就可以获取id值，无论主键是什么类型
        //var return3 = await insert.ExecuteReturnEntityAsync();
        return await insert.ExecuteReturnIdentityAsync();
    }

    public async Task<int> AddAsync(TEntity entity, Expression<Func<TEntity, object>> insertColumns = null)
    {
        var insert = _sqlSugarClient.Insertable(entity);
        if (insertColumns == null)
        {
            return await insert.ExecuteReturnIdentityAsync();
        }
        else
        {
            return await insert.InsertColumns(insertColumns).ExecuteReturnIdentityAsync();
        }
    }

    public async Task<int> AddAsync(List<TEntity> listEntity)
    => await _sqlSugarClient.Insertable(listEntity.ToArray()).ExecuteCommandAsync();

    public void AddQueue(TEntity entity) => _sqlSugarClient.Insertable(entity).AddQueue();

    public void AddQueue(TEntity entity, Expression<Func<TEntity, object>> insertColumns = null)
    {
        var insert = _sqlSugarClient.Insertable(entity);
        if (insertColumns == null)
        {
            insert.AddQueue();
        }
        else
        {
            insert.InsertColumns(insertColumns).AddQueue();
        }
    }

    public void AddQueue(List<TEntity> listEntity) => _sqlSugarClient.Insertable(listEntity.ToArray()).AddQueue();

    #endregion

    #region 删除 
    public async Task<bool> DeleteAsync(TEntity entity)
    => await _sqlSugarClient.Deleteable(entity).ExecuteCommandHasChangeAsync();

    public async Task<bool> DeleteByIdAsync(object id)
    => await _sqlSugarClient.Deleteable<TEntity>(id).ExecuteCommandHasChangeAsync();

    public async Task<bool> DeleteByIdsAsync(params object[] ids)
    => await _sqlSugarClient.Deleteable<TEntity>().In(ids).ExecuteCommandHasChangeAsync();

    public async Task<bool> DeleteAsync(Expression<Func<TEntity, bool>> whereExpression)
    => await _sqlSugarClient.Deleteable<TEntity>().Where(whereExpression).ExecuteCommandHasChangeAsync();


    public void DeleteQueue(TEntity entity)
    => _sqlSugarClient.Deleteable(entity).AddQueue();

    public void DeleteQueue(object id)
    => _sqlSugarClient.Deleteable<TEntity>(id).AddQueue();

    public void DeleteQueue(params object[] ids)
    => _sqlSugarClient.Deleteable<TEntity>().In(ids).AddQueue();

    public void DeleteQueue(Expression<Func<TEntity, bool>> whereExpression)
    => _sqlSugarClient.Deleteable<TEntity>().Where(whereExpression).AddQueue();
    #endregion

    #region 修改

    public async Task<bool> UpdateAsync(TEntity entity)
    => await _sqlSugarClient.Updateable(entity).ExecuteCommandHasChangeAsync();

    public async Task<bool> UpdateAsync(
     TEntity entity,
     Expression<Func<TEntity, object>> columnsExpression = null,
     Expression<Func<TEntity, object>> ignoreColumnsExpression = null,
     Expression<Func<TEntity, bool>> whereExpression = null)
    {
        IUpdateable<TEntity> up = _sqlSugarClient.Updateable(entity);
        if (ignoreColumnsExpression != null)
        {
            up = up.IgnoreColumns(ignoreColumnsExpression);
        }
        if (columnsExpression != null)
        {
            up = up.UpdateColumns(columnsExpression);
        }
        if (whereExpression != null)
        {
            up = up.Where(whereExpression);
        }
        return await up.ExecuteCommandHasChangeAsync();
    }

    public async Task<bool> UpdateAsync(Expression<Func<TEntity, TEntity>> columns, Expression<Func<TEntity, bool>> whereExpression)
    => await Db.Updateable<TEntity>().SetColumns(columns).Where(whereExpression).ExecuteCommandAsync() > 0;

    public void UpdateQueue(TEntity entity)
    => _sqlSugarClient.Updateable(entity).AddQueue();

    public void UpdateQueue(
     TEntity entity,
     Expression<Func<TEntity, object>> columnsExpression = null,
     Expression<Func<TEntity, object>> ignoreColumnsExpression = null,
     Expression<Func<TEntity, bool>> whereExpression = null)
    {
        IUpdateable<TEntity> up = _sqlSugarClient.Updateable(entity);
        if (ignoreColumnsExpression != null)
        {
            up = up.IgnoreColumns(ignoreColumnsExpression);
        }
        if (columnsExpression != null)
        {
            up = up.UpdateColumns(columnsExpression);
        }
        if (whereExpression != null)
        {
            up = up.Where(whereExpression);
        }
        up.AddQueue();
    }

    public void UpdateQueue(Expression<Func<TEntity, TEntity>> columns, Expression<Func<TEntity, bool>> whereExpression)
    => Db.Updateable<TEntity>().SetColumns(columns).Where(whereExpression).AddQueue();

    public async Task<bool> UpdateWithChangedAsync(TEntity entity, Expression<Func<TEntity, bool>> whereExpression = null, Expression<Func<TEntity, object>> ignoreColumnsExpression = null)
    {
        var updateColumns = entity.ChangedPropertys.ToArray();
        Checker.Argument.IsNotEmpty(updateColumns, nameof(updateColumns));

        var up = _sqlSugarClient.Updateable(entity).UpdateColumns(updateColumns);
        if (ignoreColumnsExpression != null)
        {
            up = up.IgnoreColumns(ignoreColumnsExpression);
        }
        if (whereExpression != null)
        {
            up = up.Where(whereExpression);
        }
        return await up.ExecuteCommandHasChangeAsync();
    }

    public void UpdateWithChangedQueue(TEntity entity, Expression<Func<TEntity, bool>> whereExpression = null, Expression<Func<TEntity, object>> ignoreColumnsExpression = null)
    {
        var updateColumns = entity.ChangedPropertys.ToArray();
        Checker.Argument.IsNotEmpty(updateColumns, nameof(updateColumns));

        var up = _sqlSugarClient.Updateable(entity).UpdateColumns(updateColumns);
        if (ignoreColumnsExpression != null)
        {
            up = up.IgnoreColumns(ignoreColumnsExpression);
        }
        if (whereExpression != null)
        {
            up = up.Where(whereExpression);
        }
        up.AddQueue();
    }

    #endregion

    #region 查询

    #region Any、Max、Sum、Count
    public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> expression)
    => await _sqlSugarClient.Queryable<TEntity>().AnyAsync(expression);

    public async Task<TResult> MaxAsync<TResult>(Expression<Func<TEntity, TResult>> columnExpression)
    => await _sqlSugarClient.Queryable<TEntity>().MaxAsync<TResult>(columnExpression);

    public async Task<TResult> MaxAsync<TResult>(Expression<Func<TEntity, TResult>> columnExpression, Expression<Func<TEntity, bool>> whereExpression)
    => await _sqlSugarClient.Queryable<TEntity>().Where(whereExpression).MaxAsync<TResult>(columnExpression);

    public async Task<TResult> SumAsync<TResult>(Expression<Func<TEntity, TResult>> columnExpression)
    => await _sqlSugarClient.Queryable<TEntity>().SumAsync<TResult>(columnExpression);

    public async Task<TResult> SumAsync<TResult>(Expression<Func<TEntity, TResult>> columnExpression, Expression<Func<TEntity, bool>> whereExpression)
    => await _sqlSugarClient.Queryable<TEntity>().Where(whereExpression).SumAsync<TResult>(columnExpression);

    public async Task<int> CountAsync(Expression<Func<TEntity, bool>> whereExpression)
    => await _sqlSugarClient.Queryable<TEntity>().CountAsync(whereExpression);

    #endregion

    #region Get

    public async Task<TEntity> GetAsync(object objId)
    => await _sqlSugarClient.Queryable<TEntity>().In(objId).SingleAsync();

    public async Task<TEntity> GetAsync(object objId, bool blnUseCache = false)
    => await _sqlSugarClient.Queryable<TEntity>().WithCacheIF(blnUseCache).In(objId).SingleAsync();

    public async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> whereExpression)
    => await _sqlSugarClient.Queryable<TEntity>().WhereIF(whereExpression != null, whereExpression).SingleAsync();

    public async Task<TResult> GetAsync<TResult>(Expression<Func<TEntity, TResult>> columnExpression, Expression<Func<TEntity, bool>> whereExpression)
    => await _sqlSugarClient.Queryable<TEntity>().Where(whereExpression).Select(columnExpression).SingleAsync();

    public async Task<TEntity> GetCheckAsync(object objId, string throwMessage = "数据不存在或已被删除")
    {
        var model = await GetAsync(objId);

        if (model != null) return model;

        throw new BusinessException(throwMessage);
    }

    public async Task<TEntity> GetCheckAsync(Expression<Func<TEntity, bool>> whereExpression, string throwMessage = "数据不存在或已被删除")
    {
        var model = await GetAsync(whereExpression);

        if (model != null) return model;

        throw new BusinessException(throwMessage);
    }

    public async Task<TEntity> GetFirstAsync(Expression<Func<TEntity, bool>> whereExpression)
    => await _sqlSugarClient.Queryable<TEntity>().Where(whereExpression).FirstAsync();

    public async Task<TEntity> GetFirstAsync(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, object>> orderByExpression, bool isAsc = true)
    => await _sqlSugarClient.Queryable<TEntity>().Where(whereExpression).OrderBy(orderByExpression, isAsc ? OrderByType.Asc : OrderByType.Desc).FirstAsync();

    #endregion

    #region List 

    public async Task<List<TEntity>> QueryAsync() => await _sqlSugarClient.Queryable<TEntity>().ToListAsync();

    public async Task<List<TEntity>> QueryAsync(params object[] lstIds)
    => await _sqlSugarClient.Queryable<TEntity>().In(lstIds).ToListAsync();

    public async Task<List<TEntity>> QueryAsync(Expression<Func<TEntity, bool>> whereExpression)
    => await _sqlSugarClient.Queryable<TEntity>().WhereIF(whereExpression != null, whereExpression).ToListAsync();

    public async Task<List<TEntity>> QueryAsync(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, object>> orderByExpression, bool isAsc = true)
    => await _sqlSugarClient.Queryable<TEntity>().OrderByIF(orderByExpression != null, orderByExpression, isAsc ? OrderByType.Asc : OrderByType.Desc).WhereIF(whereExpression != null, whereExpression).ToListAsync();

    public async Task<List<TEntity>> QueryAsync(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, object>> orderByExpression, bool isAsc = true, int intTop = 10)
    => await _sqlSugarClient.Queryable<TEntity>()
        .OrderByIF(orderByExpression != null, orderByExpression, isAsc ? OrderByType.Asc : OrderByType.Desc)
        .WhereIF(whereExpression != null, whereExpression).Take(intTop).ToListAsync();

    public async Task<List<TEntity>> QueryAsync(Expression<Func<TEntity, TEntity>> columnExpression, Expression<Func<TEntity, bool>> whereExpression)
    => await _sqlSugarClient.Queryable<TEntity>().Where(whereExpression).Select(columnExpression).ToListAsync();
    public async Task<List<TEntity>> QueryAsync(Expression<Func<TEntity, TEntity>> columnExpression, Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, object>> orderByExpression, bool isAsc = true)
    => await _sqlSugarClient.Queryable<TEntity>()
        .OrderByIF(orderByExpression != null, orderByExpression, isAsc ? OrderByType.Asc : OrderByType.Desc)
        .Where(whereExpression).Select(columnExpression).ToListAsync();

    public async Task<List<TResult>> QueryAsync<TResult>(Expression<Func<TEntity, TResult>> expression)
    => await _sqlSugarClient.Queryable<TEntity>().Select(expression).ToListAsync();
    public async Task<List<TResult>> QueryAsync<TResult>(Expression<Func<TEntity, TResult>> columnExpression, Expression<Func<TEntity, bool>> whereExpression)
    => await _sqlSugarClient.Queryable<TEntity>()
        .Where(whereExpression)
        .Select(columnExpression).ToListAsync();
    public async Task<List<TResult>> QueryAsync<TResult>(Expression<Func<TEntity, TResult>> columnExpression, Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, object>> orderByExpression, bool isAsc = true)
    => await _sqlSugarClient.Queryable<TEntity>()
        .OrderByIF(orderByExpression != null, orderByExpression, isAsc ? OrderByType.Asc : OrderByType.Desc)
        .Where(whereExpression)
        .Select(columnExpression).ToListAsync();


    #endregion

    #region Mapping 
    public async Task MappingAsync<T>(T entity, Func<T, long> getIDFun, Action<T, TEntity> mappingAction) where T : class
    {
        if (entity == null) return;
        var id = getIDFun(entity);
        if (id > 0)
        {
            var obj = await GetAsync(id);
            if (obj != null)
            {
                mappingAction(entity, obj);
            }
        }
    }

    public async Task MappingAsync<T>(IList<T> list, Func<T, long> getIDFun, Action<T, TEntity> mappingAction) where T : class
    {
        if (list == null || list.Count < 1) return;
        var ids = list.Select(a => getIDFun(a)).Where(a => a > 0).Select(a => a).Distinct();
        if (!ids.Any()) return;
        var dicData = await QueryDictionaryAsync(ids);
        foreach (var item in list)
        {
            var id = getIDFun(item);
            if (dicData.ContainsKey(id))
            {
                mappingAction(item, dicData[id]);
            }
        }
    }

    public async Task MappingAsync<T>(T entity, Func<T, IEnumerable<long>> getIDFun, Action<T, IEnumerable<TEntity>> mappingAction) where T : class
    {
        if (entity == null) return;
        var allIDs = getIDFun(entity).Where(a => a > 0).Select(a => a).Distinct();
        if (allIDs.Any())
        {
            var dicData = await QueryDictionaryAsync(allIDs);
            var valueList = new List<TEntity>();
            foreach (var id in allIDs)
            {
                if (dicData.ContainsKey(id))
                {
                    valueList.Add(dicData[id]);
                }
            }

            mappingAction(entity, valueList);
        }
    }

    public async Task MappingAsync<T>(IList<T> list, Func<T, IEnumerable<long>> getIDFun, Action<T, IEnumerable<TEntity>> mappingAction) where T : class
    {
        if (list == null || list.Count < 1) return;
        var allIDs = list.SelectMany(a => getIDFun(a) ?? Enumerable.Empty<long>()).Where(a => a > 0).Select(a => a);
        if (!allIDs.Any()) return;
        var dicData = await QueryDictionaryAsync(allIDs);
        foreach (var item in list)
        {
            var ids = getIDFun(item).Where(id => id != 0).Select(id => id).ToList();
            var valueList = new List<TEntity>();
            foreach (var id in ids)
            {
                if (dicData.ContainsKey(id))
                {
                    valueList.Add(dicData[id]);
                }
            }
            mappingAction(item, valueList);
        }
    }

    public async Task MappingAsync<T>(T entity, Func<T, long?> getIDFun, Action<T, TEntity> mappingAction) where T : class
    {
        if (entity == null) return;
        var id = getIDFun(entity);
        if (id.HasValue && id.Value > 0)
        {
            var obj = await GetAsync(id);
            if (obj != null)
            {
                mappingAction(entity, obj);
            }
        }
    }

    public async Task MappingAsync<T>(IList<T> list, Func<T, long?> getIDFun, Action<T, TEntity> mappingAction) where T : class
    {
        if (list == null || list.Count < 1) return;
        var ids = list.Select(a => getIDFun(a)).Where(a => a.HasValue && a.Value > 0).Select(a => a.Value);
        if (!ids.Any()) return;
        var dicData = await QueryDictionaryAsync(ids);
        foreach (var item in list)
        {
            var id = getIDFun(item);
            if (id.HasValue && dicData.ContainsKey(id.Value))
            {
                mappingAction(item, dicData[id.Value]);
            }
        }
    }

    public async Task MappingAsync<T>(T entity, Func<T, IEnumerable<long?>> getIDFun, Action<T, IEnumerable<TEntity>> mappingAction) where T : class
    {
        if (entity == null) return;
        var allIDs = getIDFun(entity).Where(a => a.HasValue && a.Value > 0).Select(a => a.Value).Distinct();
        if (allIDs.Any())
        {
            var dicData = await QueryDictionaryAsync(allIDs);
            var valueList = new List<TEntity>();
            foreach (var id in allIDs)
            {
                if (dicData.ContainsKey(id))
                {
                    valueList.Add(dicData[id]);
                }
            }

            mappingAction(entity, valueList);
        }
    }

    public async Task MappingAsync<T>(IList<T> list, Func<T, IEnumerable<long?>> getIDFun, Action<T, IEnumerable<TEntity>> mappingAction) where T : class
    {
        if (list == null || list.Count < 1) return;
        var allIDs = list.SelectMany(a => getIDFun(a) ?? Enumerable.Empty<long?>()).Where(a => a.HasValue && a.Value > 0).Select(a => a.Value);
        if (!allIDs.Any()) return;
        var dicData = await QueryDictionaryAsync(allIDs);
        foreach (var item in list)
        {
            var ids = getIDFun(item).Where(id => id != 0).Select(id => id).ToList();
            var valueList = new List<TEntity>();
            foreach (var id in ids)
            {
                if (dicData.ContainsKey(id))
                {
                    valueList.Add(dicData[id]);
                }
            }
            mappingAction(item, valueList);
        }
    }

    public async Task MappingAsync<T>(T entity, Func<T, string> getIDFun, Action<T, TEntity> mappingAction) where T : class
    {
        if (entity == null) return;
        var id = getIDFun(entity);
        if (id.IsNotNullOrWhiteSpace())
        {
            var obj = await GetAsync(id);
            if (obj != null)
            {
                mappingAction(entity, obj);
            }
        }
    }

    public async Task MappingAsync<T>(IList<T> list, Func<T, string> getIDFun, Action<T, TEntity> mappingAction) where T : class
    {
        if (list == null || list.Count < 1) return;
        var ids = list.Select(a => getIDFun(a)).Where(a => a.IsNotNullOrWhiteSpace()).Select(a => a);
        if (!ids.Any()) return;
        var dicData = await QueryDictionaryAsync(ids);
        foreach (var item in list)
        {
            var id = getIDFun(item);
            if (id.IsNotNullOrWhiteSpace() && dicData.ContainsKey(id))
            {
                mappingAction(item, dicData[id]);
            }
        }
    }

    public async Task MappingAsync<T>(T entity, Func<T, IEnumerable<string>> getIDFun, Action<T, IEnumerable<TEntity>> mappingAction) where T : class
    {
        if (entity == null) return;
        var allIDs = getIDFun(entity).Where(a => a.IsNotNullOrWhiteSpace()).Select(a => a).Distinct();
        if (allIDs.Any())
        {
            var dicData = await QueryDictionaryAsync(allIDs);
            var valueList = new List<TEntity>();
            foreach (var id in allIDs)
            {
                if (dicData.ContainsKey(id))
                {
                    valueList.Add(dicData[id]);
                }
            }

            mappingAction(entity, valueList);
        }
    }

    public async Task MappingAsync<T>(IList<T> list, Func<T, IEnumerable<string>> getIDFun, Action<T, IEnumerable<TEntity>> mappingAction) where T : class
    {
        if (list == null || list.Count < 1) return;
        var allIDs = list.SelectMany(a => getIDFun(a) ?? Enumerable.Empty<string>()).Where(id => id.IsNotNullOrWhiteSpace()).Select(id => id);
        if (!allIDs.Any()) return;
        var dicData = await QueryDictionaryAsync(allIDs);
        foreach (var item in list)
        {
            var ids = getIDFun(item).Where(id => id.IsNotNullOrWhiteSpace()).Select(id => id).ToList();
            var valueList = new List<TEntity>();
            foreach (var id in ids)
            {
                if (dicData.ContainsKey(id))
                {
                    valueList.Add(dicData[id]);
                }
            }
            mappingAction(item, valueList);
        }
    }

    #endregion

    #region sql query
    /// <summary>根据sql语句查询</summary>
    /// <param name="strSql">完整的sql语句</param>
    /// <param name="parameters">参数</param>
    /// <returns>泛型集合</returns>
    public async Task<List<T>> QuerySqlAsync<T>(string strSql, params SugarParameter[] parameters) where T : new()
    => await _sqlSugarClient.Ado.SqlQueryAsync<T>(strSql, parameters);
    #endregion

    #region DataTable
    /// <summary>根据sql语句查询</summary>
    /// <param name="strSql">完整的sql语句</param>
    /// <param name="parameters">参数</param>
    /// <returns>DataTable</returns>
    public async Task<DataTable> QueryTableAsync(string strSql, params SugarParameter[] parameters)
    => await _sqlSugarClient.Ado.GetDataTableAsync(strSql, parameters);
    #endregion 

    #region Dictionary
    public async Task<Dictionary<object, TEntity>> QueryDictionaryAsync(params object[] lstIds)
    {
        var list = await QueryAsync(lstIds);
        var keyProperty = this.Db.GetEntityPrimaryKeyProperty<TEntity>();
        return list.ToDictionary(a => keyProperty.PropertyInfo.GetValue(a), a => a);
    }
    #endregion 

    #endregion

    public async Task<int> SaveQueuesAsync(bool isTran = true)
    => await _sqlSugarClient.SaveQueuesAsync(isTran);

}
